/*
 * @Author: Wangjun
 * @Date: 2023-03-21 09:11:19
 * @LastEditTime: 2023-11-16 10:47:06
 * @LastEditors: Wangjun
 * @Description:
 * @FilePath: \libs\expr\node.go
 * hnxr
 */
package expr

import (
	"bytes"
	"errors"
	"go/ast"
	"go/token"
	"strconv"
)

type Get func(string) (float64, error)

type Number float64

func (m Number) String() string {
	return strconv.FormatFloat(float64(m), 'f', -1, 64)
}

func (m Number) Eval() (float64, error) {
	return float64(m), nil
}

func ParseNumber(b *ast.BasicLit) (n Number, err error) {
	switch b.Kind {
	case token.INT:
		ival, err := strconv.Atoi(b.Value)
		if err != nil {
			return 0, err
		}
		return Number(ival), nil
	case token.FLOAT:
		fval, err := strconv.ParseFloat(b.Value, 64)
		if err != nil {
			return 0, err
		}
		return Number(fval), nil
	}

	return 0, errors.New("不是有效的数字")
}

type unaryNot struct {
}

func (m *unaryNot) String() string {
	return "!"
}

func (m *unaryNot) Eval(x float64) (float64, error) {
	if int(x) == 0 {
		return 1, nil
	}
	return 0, nil
}

type unarySub struct {
}

func (m *unarySub) String() string {
	return "-"
}

func (m *unarySub) Eval(x float64) (float64, error) {
	return -x, nil
}

type UnaryOPer interface {
	String() string
	Eval(x float64) (float64, error)
}
type UnaryExpr struct {
	x  Evaler
	op UnaryOPer
}

func (m *UnaryExpr) String() string {
	return ""
}

func (m *UnaryExpr) Eval() (float64, error) {
	x, err := m.x.Eval()
	if err != nil {
		return 0, err
	}
	return m.op.Eval(x)
}

// () 括号运算
type ParenExpr struct {
	x Evaler
}

func (m *ParenExpr) Eval() (float64, error) {
	return m.x.Eval()
}

func (m *ParenExpr) String() string {
	return m.x.String()
}

type BinaryExpr struct {
	x, y Evaler
	op   OPer
}

func (m *BinaryExpr) String() string {
	return m.op.String()
}

func (m *BinaryExpr) Eval() (float64, error) {
	x, err := m.x.Eval()
	if err != nil {
		return 0, err
	}
	y, err := m.y.Eval()
	if err != nil {
		return 0, err
	}

	return m.op.Eval(x, y)
}

type Ident struct {
	ctx  *Context
	name string
}

func (m *Ident) Eval() (float64, error) {
	return m.ctx.get(m.name)
}

func (m *Ident) String() string {
	return m.name
}

type CallExpr struct {
	args   []Evaler
	fvals  []float64
	name   string
	method Function
}

func (m *CallExpr) String() string {
	buf := bytes.NewBufferString(m.name)
	buf.WriteByte('(')
	for i, arg := range m.args {
		if i != 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(arg.String())
	}
	buf.WriteByte(')')
	return buf.String()
}

func (m *CallExpr) Eval() (float64, error) {
	var err error
	for i, e := range m.args {
		m.fvals[i], err = e.Eval()
		if err != nil {
			return 0, err
		}
	}

	return m.method(m.fvals...)
}
